package com.qd.pay.service.order;

import com.cdqidi.core.weixinmanager.domain.OrgWxConfigDTO;
import com.cdqidi.core.weixinmanager.service.OrgWxConfigService;
import com.cdqidi.exception.ApiException;
import com.cdqidi.util.DateUtil;
import com.cdqidi.util.IpHelper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.jfinal.kit.HttpKit;
import com.jfinal.weixin.sdk.api.PaymentApi;
import com.jfinal.weixin.sdk.kit.PaymentKit;
import com.qd.pay.config.PayInfoConfig;
import com.qd.pay.domain.*;
import com.qd.pay.model.*;
import com.qd.pay.service.*;
import com.qd.pay.util.AesUtil;
import com.qd.pay.util.IdUtil;
import com.qd.pay.util.RandomCharData;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 微信订单
 */
@Component
@AllArgsConstructor
@Slf4j
public class WxOrderService {
    private static final String FAIL = "FAIL";
    private static final String ERR_CODE = "错误代码:";

    private final OrgWxConfigService orgWxConfigService;
    private final OrderWxService orderWxService;
    private final OrderService orderService;
    private final OrderRefundService orderRefundService;
    private final OrderReportService orderReportService;
    private final OrderBodyService orderBodyService;
    private final OrderReportNotifyService orderReportNotifyService;
    private final OrderRefundReportService orderRefundReportService;
    private final OrderRefundWxReportService orderRefundWxReportService;
    private final OrderRespService orderRespService;
    private final PayInfoConfig payInfo;
    private final HttpServletRequest httpServletRequest;
    private final ObjectMapper objectMapper;

    @Transactional(rollbackFor = Exception.class)
    public Map<String, String> createWxOrder(OrderDTO dto, Order order, OrderBody body) {
        final OrgWxConfigDTO orgWxConfig = orgWxConfigService.getByIdDto(dto.getAppId());
        OrderWx data = new OrderWx();
        data.setOrderId(order.getOrderId());
        data.setMchId(orgWxConfig.getMchId());
        data.setWxorgid(orgWxConfig.getAppId());
        data.setWxAccountType(orgWxConfig.getWxAccountType());
        data.setOpenid(dto.getOpenid());
        data.setTradeType(dto.getTradeType());
        data.setSignType("MD5");
        String xmlResult = sendWxOrder(dto, order, dto.getAppId(), orgWxConfig, data);
        body.setContent(xmlResult);
        final Map<String, String> map = wxOrderResp(orgWxConfig, xmlResult, order);
        orderWxService.save(data);
        data.freeData();

        orgWxConfig.freeData();
        return map;

    }

    public void closeWxOrder(String appId, Order order) {
        final OrgWxConfigDTO orgWxConfig = orgWxConfigService.getByIdDto(appId);
        Map<String, String> map = PaymentApi.closeOrder(appId, orgWxConfig.getMchId(), orgWxConfig.getApiKey(), order.getOutTradeNo());
        WxRespDTO wxRespDto = new WxRespDTO(map);
        try {
            if (wxRespDto.getReturnCode().equals(FAIL)) {
                throw new ApiException(wxRespDto.getReturnMsg());
            }
            if (wxRespDto.getResultCode().equals(FAIL)) {
                String resultMsg = ERR_CODE + wxRespDto.getErrCode() + ",错误描述: " + wxRespDto.getErrCodeDes();
                throw new ApiException(resultMsg);
            }
            order.setState(1).setUpdateTime(LocalDateTime.now());
            orderService.updateById(order);
            String xml = PaymentKit.toXml(map);
            OrderBody orderBody = new OrderBody().setBid(order.getOrderId()).setType(OrderBodyDTO.ORDER_TYPE_ORDER_CLOSE).setContent(xml).setAddTime(order.getUpdateTime());
            orderBodyService.save(orderBody);
            orderBody.freeData();
            order.freeData();
        } finally {
            orgWxConfig.freeData();
            map.clear();
            wxRespDto.freeData();
        }
    }

    public void checkRefund(Order order, OrderRefundDTO dto) {
        /*
         *当前订单已经发起过退款，此时需要到微信端验证当前订单是否已经退款，退款的总金额是好多，如果已经达到当前订单的金额
         * 那么不能继续退款
         */
        List<OrderRefundReportDTO> list = refundQuery(order, dto.getAppId());
        if (!list.isEmpty()) {
            Integer fee = totalRefundFee(list);
            if (fee >= order.getPayAmount().intValue()) {
                throw new ApiException("退款总金额超过当前订单支付金额，不能退款");
            }
            list.clear();
        }
    }

    public void orderRefund(OrderRefundDTO dto, Order order, OrderRefund orderRefund, OrderBody orderBody) {
        Map<String, String> params = wxOrderRefund(dto, order);
        wxRespVerify(params);
        String transactionId = params.get("transaction_id");
        String outRefundNo = params.get("out_refund_no");
        /*
         * 退款金额
         */
        String refundFee = params.get("refund_fee");
        /*
         * 订单金额
         */
        String totalFee = params.get("total_fee");

        int orderTotalFee = Integer.parseInt(totalFee);

        if (orderTotalFee != order.getPayAmount().intValue()) {
            throw new ApiException("金额不匹配，订单金额： " + (order.getPayAmount().intValue() / 100) + "元，退款时返回的总金额： " + (orderTotalFee / 100) + "元");
        }

        orderRefund.setTransactionId(transactionId);
        orderRefund.setOutRefundNo(outRefundNo);
        orderRefund.setRefundFee(new BigDecimal(refundFee));
        orderBody.setContent(PaymentKit.toXml(params));
    }

    private Map<String, String> wxOrderRefund(OrderRefundDTO dto, Order order) {
        String appId = dto.getAppId();
        final OrgWxConfigDTO orgWxConfig = orgWxConfigService.getByIdDto(appId);
        Map<String, String> params = new HashMap<>(10);
        params.put("appid", appId);
        params.put("mch_id", orgWxConfig.getMchId());
        params.put("out_trade_no", order.getOutTradeNo());
        params.put("out_refund_no", IdUtil.getOutRefundNo());
        params.put("total_fee", order.getPayAmount() + "");
        params.put("refund_fee", dto.getRefundFee() + "");
        params.put("notify_url", payInfo.getDomain() + "/payManager/payOrder/payWxNotify");
        File file = orderBodyService.getCertificate(orgWxConfig.getAppId());
        log.info("certificate: {}", file.getPath());
        return PaymentApi.refund(params, orgWxConfig.getApiKey(), file.getPath());
    }

    private Integer totalRefundFee(List<OrderRefundReportDTO> list) {
        BigDecimal total = new BigDecimal(0);
        for (OrderRefundReportDTO orderRefundReportDto : list) {
            if (orderRefundReportDto.getRefundStatus().equals(OrderService.SUCCESS)) {
                total.add(orderRefundReportDto.getRefundFee());
            }
        }
        return total.intValue();
    }

    /**
     * 退款信息
     *
     * @param order 订单信息
     * @return 退款列表
     */
    private List<Map<String, Object>> getRefund(Order order, String appId) {
        List<Map<String, Object>> cList = new ArrayList<>();
        /*
         * 转入退款
         */
        String resultCode = "NORESULT";
        String resultMsg = "没有查询到退款结果";
        List<OrderRefundReportDTO> list = refundQuery(order, appId);
        Map<String, Object> map;
        if (!list.isEmpty()) {
            for (OrderRefundReportDTO reportDto : list) {
                map = new HashMap<>(5);
                resultCode = reportDto.getRefundStatus();
                if (resultCode.equals(OrderService.SUCCESS)) {
                    resultMsg = "退款成功";
                    map.put("refundFeeEndTime", reportDto.getTimeEnd());
                }
                map.put("refundFee", reportDto.getRefundFee());
                if (resultCode.equals(FAIL)) {
                    resultMsg = "退款失败";
                }
                if ("REFUNDCLOSE".equals(resultCode)) {
                    resultMsg = "退款关闭";
                }
                if ("PROCESSING".equals(resultCode)) {
                    resultMsg = "退款处理中";
                }
                if ("CHANGE".equals(resultCode)) {
                    resultMsg = "退款异常";
                }
                map.put("orderRefundState", resultCode);
                map.put("orderRefundMsg", resultMsg);
                map.put("orderRefundNo", reportDto.getRefundNo());
                reportDto.freeData();
                cList.add(map);
            }
        } else {
            map = new HashMap<>(2);
            map.put("orderRefundState", resultCode);
            map.put("orderRefundMsg", resultMsg);
            cList.add(map);
        }
        return cList;
    }

    public List<OrderRefundReportDTO> refundQuery(Order order, String appId) {
        List<OrderRefundReportDTO> list = new ArrayList<>();
        if (!StringUtils.hasLength(appId)) {
            throw new ApiException("支付配置异常");
        }
        final OrgWxConfigDTO orgWxConfig = orgWxConfigService.getByIdDto(appId);
        Map<String, String> params = PaymentApi.refundQueryByOutTradeNo(appId, orgWxConfig.getMchId(), orgWxConfig.getApiKey(), order.getOutTradeNo());
        log.info("params: {}", params);
        wxRespVerify(params);
        if (PaymentKit.verifyNotify(params, orgWxConfig.getApiKey())) {
            //总金额
            String totalFee = params.get("total_fee");
            int payAmount = getPayAmount(totalFee);
            check(order, orgWxConfig, params, payAmount);

            //退款笔数
            String refundCount = params.get("refund_count");
            int intRefundCount = Integer.parseInt(refundCount);
            OrderRefundReportDTO orderRefundReport;
            LocalDateTime addTime = LocalDateTime.now();
            for (int i = 0; i < intRefundCount; i++) {
                orderRefundReport = getOrderRefundReportDto(order, params, addTime, i);
                createRefundWxReport(params, intRefundCount, orderRefundReport, i);
                list.add(orderRefundReport);
            }
            return list;
        } else {
            throw new ApiException("签名验证失败");
        }
    }

    /**
     * 回执信息
     *
     * @param order 订单信息
     * @return 回执状态
     */
    public Map<String, Object> getOrderReport(Order order, String appId) {
        Map<String, Object> childMap = new HashMap<>();
        String orderState = "NORESULT";
        String stateMsg = "没有查询到支付结果";
        /*
         *查看支付结果
         */
        OrderReport orderReport = new OrderReport().setOrderId(order.getOrderId());
        OrderReport report = orderReportService.get(orderReport);
        orderReport.freeData();
        if (null == report) {
            //没有支付记录，此时调用微信端查看支付结果
            report = orderQuery(order, appId);
            if (null != report) {
                orderState = report.getResultCode();
            }
        } else {
            orderState = report.getResultCode();
        }
        switch (orderState) {
            case "SUCCESS":
                stateMsg = "支付成功";
                if (null != report) {
                    childMap.put("payTimeEnd", report.getTimeEnd());
                }
                break;
            case "REFUND":
                stateMsg = "转入退款";
                break;
            case "NOTPAY":
                stateMsg = "未支付";
                break;
            case "CLOSED":
                stateMsg = "已关闭";
                break;
            case "REVOKED":
                stateMsg = "已撤销";
                break;
            case "USERPAYING":
                stateMsg = "用户支付中";
                break;
            case "PAYERROR":
                stateMsg = "支付失败";
                break;
            default:
                break;
        }
        childMap.put("orderState", orderState);
        childMap.put("orderStateMsg", stateMsg);
        return childMap;
    }

    public OrderReport orderQuery(Order order, String appId) {
        if (!StringUtils.hasLength(appId)) {
            throw new ApiException("支付配置异常");
        }
        final OrgWxConfigDTO orgWxConfig = orgWxConfigService.getByIdDto(appId);
        Map<String, String> params = PaymentApi.queryByOutTradeNo(appId, orgWxConfig.getMchId(), orgWxConfig.getApiKey(), order.getOutTradeNo());
        wxRespVerify(params);
        if (PaymentKit.verifyNotify(params, orgWxConfig.getApiKey())) {
            log.info("orderQuery: {}", PaymentKit.toXml(params));
            String tradeState = params.get("trade_state");
            //总金额
            String totalFee = params.get("total_fee");
            int payAmount = getPayAmount(totalFee);
            check(order, orgWxConfig, params, payAmount);

            OrderReport report = new OrderReport();
            report.setOrderId(order.getOrderId());
            try {
                if (tradeState.equals(OrderService.SUCCESS)) {
                    //支付完成时间
                    String timeEnd = params.get("time_end");
                    LocalDateTime timeEndLocalDateTime = DateUtil.getDate(timeEnd);
                    report.setTimeEnd(timeEndLocalDateTime);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            report.setPayAmount(new BigDecimal(payAmount));
            report.setResultCode(tradeState);
            return report;
        } else {
            throw new ApiException("签名错误");
        }
    }

    public void wxOrderState(String appId, boolean isOrderResp, boolean isOrderReport, boolean isOrderRefund, Order order, Map<String, Object> map) {
        Map<String, Object> orderInfo = getOrderInfo(order);
        map.put("orderInfo", orderInfo);

        if (isOrderResp) {
            Map<String, Object> orderResp = getOrderResp(order);
            map.put("orderRespInfo", orderResp);
        }

        if (isOrderReport) {
            Map<String, Object> resMap = getOrderReport(order, appId);
            map.put("orderReportInfo", resMap);
        }

        if (isOrderRefund) {
            boolean b = orderRefundService.isExistsByOrderId(order.getOrderId());
            if (b) {
                List<Map<String, Object>> list = getRefund(order, appId);
                map.put("orderRefundList: ", list);
            }
        }
    }

    /**
     * 订单信息
     *
     * @param order 订单
     * @return 订单部分信息
     */
    private Map<String, Object> getOrderInfo(Order order) {
        Map<String, Object> childMap = new HashMap<>(6);
        childMap.put("orderId", order.getOrderId());
        childMap.put("thirdPartyOrder", order.getThirdPartyOrder());
        childMap.put("payAmount", order.getPayAmount());
        childMap.put("sendTime", order.getSendTime());
        childMap.put("type", order.getType());
        childMap.put("state", order.getState());
        return childMap;
    }

    /**
     * 支付信息
     *
     * @param order 订单信息
     * @return 支付结果
     */
    public Map<String, Object> getOrderResp(Order order) {
        Map<String, Object> childMap = new HashMap<>(3);
        OrderResp one = orderRespService.getOne(order.getOrderId());
        if (null == one) {
            String resultCode = "NORESULT";
            String resultMsg = "没有查询到支付状态";
            childMap.put("orderRespCode", resultCode);
            childMap.put("orderRespMsg", resultMsg);
        } else {
            childMap.put("orderRespCode", one.getPayCode());
            childMap.put("orderRespMsg", one.getErrMsg());
            childMap.put("timeStart", one.getTimeStart());
            one.freeData();
        }
        return childMap;
    }

    private String sendWxOrder(OrderDTO dto, Order order, String appId, OrgWxConfigDTO orgWxConfig, OrderWx data) {
        final String nonceStr = System.currentTimeMillis() + RandomCharData.createRandomCharData(12);
        Map<String, String> params = new HashMap<>(12);
        params.put("appid", appId);
        params.put("mch_id", data.getMchId());
        params.put("body", order.getBody());
        params.put("out_trade_no", order.getOutTradeNo());
        params.put("total_fee", order.getPayAmount() + "");
        params.put("spbill_create_ip", order.getIpAddress());
        params.put("trade_type", dto.getTradeType());
        params.put("nonce_str", nonceStr);
        params.put("notify_url", payInfo.getDomain() + "/payManager/payOrder/payWxNotify");
        params.put("openid", dto.getOpenid());
        params.put("attach", order.getOrderId());
        //商户平台设置的key
        String sign = PaymentKit.createSign(params, orgWxConfig.getApiKey());
        params.put("sign", sign);

        data.setSign(sign);
        data.setNonceStr(nonceStr);

        return PaymentApi.pushOrder(params);
    }

    private Map<String, String> wxOrderResp(OrgWxConfigDTO orgWxConfig, String xmlResult, Order order) {
        Map<String, String> result = PaymentKit.xmlToMap(xmlResult);
        WxRespDTO wxOrderResp = new WxRespDTO(result);
        try {
            if (wxOrderResp.getReturnCode().equals(FAIL)) {
                throw new ApiException(3, wxOrderResp.getReturnMsg());
            }
            if (wxOrderResp.getResultCode().equals(FAIL)) {
                String resultMsg = ERR_CODE + wxOrderResp.getErrCode() + ",错误描述: " + wxOrderResp.getErrCodeDes();
                throw new ApiException(3, resultMsg);
            }
            // 以下字段在return_code 和result_code都为SUCCESS的时候有返回
            String prepayId = result.get("prepay_id");
            long timeStamp = System.currentTimeMillis();
            String nonceStr = System.currentTimeMillis() + RandomCharData.createRandomCharData(19);

            Map<String, String> packageParams = new HashMap<>(8);
            packageParams.put("appId", orgWxConfig.getAppId());
            packageParams.put("timeStamp", (timeStamp / 1000) + "");
            packageParams.put("nonceStr", nonceStr);
            packageParams.put("package", "prepay_id=" + prepayId);
            packageParams.put("signType", "MD5");
            String packageSign = PaymentKit.createSign(packageParams, orgWxConfig.getApiKey());

            packageParams.put("paySign", packageSign);
            packageParams.put("orderId", order.getOrderId());
            packageParams.put("thirdPartyOrder", order.getThirdPartyOrder());

            return packageParams;
        } finally {
            result.clear();
            wxOrderResp.freeData();
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public void payNotify() {
        final String realIp = IpHelper.getIpAddress(httpServletRequest);
        log.info("pay_notify,Ip: {}", realIp);

        String xmlMsg = HttpKit.readData(httpServletRequest);
        log.info("支付通知={}", xmlMsg);
        if (!StringUtils.hasLength(xmlMsg)) {
            log.error("支付通知为空");
            throw new ApiException("支付通知为空");
        }
        Map<String, String> params = PaymentKit.xmlToMap(xmlMsg);
        String reqInfo = params.get("req_info");
        if (StringUtils.hasLength(reqInfo)) {
            saveRefundReport(xmlMsg);
        } else {
            saveReport(xmlMsg);
        }
        params.clear();
    }

    private void saveRefundReport(String xmlMsg) {
        Map<String, String> params = PaymentKit.xmlToMap(xmlMsg);
        wxRespVerify(params);
        String appId = params.get("appid");
        String reqInfo = params.get("req_info");
        OrgWxConfigDTO orgWxConfig = orgWxConfigService.getByIdDto(appId);
        OrderRefundReportDTO refundReportDto = notifyWxRefund(reqInfo, orgWxConfig);
        saveRefundReport(refundReportDto.getXml(), refundReportDto);
        params.clear();
        orgWxConfig.freeData();
        refundReportDto.freeData();
    }

    private void saveRefundReport(String xmlMsg, OrderRefundReportDTO refundReportDto) {
        boolean isExists = orderRefundReportService.isExistsByOrderIdAndRefundNo(refundReportDto.getOrderId(), refundReportDto.getRefundNo());
        if (!isExists) {
            saveRefundReportNotify(refundReportDto);

            OrderBody orderBody = new OrderBody()
                    .setBid(refundReportDto.getOrderId())
                    .setContent(xmlMsg)
                    .setAddTime(refundReportDto.getAddTime())
                    .setType(OrderBodyDTO.ORDER_TYPE_ORDER_REFUND);
            orderBodyService.save(orderBody);
            orderBody.freeData();

            OrderRefundWxReportDTO orderRefundWxReportDto = refundReportDto.getOrderRefundWxReportDto();
            OrderRefundWxReport orderRefundWxReport = orderRefundWxReportService.dtoToModel(orderRefundWxReportDto);
            orderRefundWxReportService.save(orderRefundWxReport);
            orderRefundWxReport.freeData();

            OrderRefundReport orderRefundReport = orderRefundReportService.dtoToModel(refundReportDto);
            orderRefundReportService.save(orderRefundReport);
            orderRefundReport.freeData();
        }
    }

    private void saveReport(String xmlMsg) {
        Map<String, String> params = PaymentKit.xmlToMap(xmlMsg);
        wxRespVerify(params);
        String appId = params.get("appid");
        OrgWxConfigDTO orgWxConfig = orgWxConfigService.getByIdDto(appId);
        OrderReport orderReport = notifyWxOrder(orgWxConfig, params);
        saveOrderReport(xmlMsg, orderReport);
        params.clear();
        orgWxConfig.freeData();
        orderReport.freeData();
    }

    public void saveOrderReport(String xmlMsg, OrderReport orderReport) {
        boolean isExists = orderReportService.isExists(orderReport.getOrderId());
        if (!isExists) {
            OrderBody orderBody = new OrderBody()
                    .setBid(orderReport.getOrderId())
                    .setContent(xmlMsg)
                    .setAddTime(orderReport.getAddTime())
                    .setType(OrderBodyDTO.ORDER_TYPE_ORDER_REPORT);
            orderBodyService.save(orderBody);
            orderBody.freeData();
            orderReportService.save(orderReport);
            saveRefundReportNotify(orderReport);
        }
    }

    private void saveRefundReportNotify(OrderReport orderReport) {
        Order order = orderService.getById(orderReport.getOrderId());
        String notifyUrl = order.getNotifyUrl();
        if (StringUtils.hasLength(notifyUrl)) {
            OrderReportNotify data = new OrderReportNotify();
            data.setOrderId(order.getOrderId());
            data.setAddTime(LocalDateTime.now());
            data.setOutTradeNo(order.getOutTradeNo());
            data.setThirdPartyOrder(order.getThirdPartyOrder());
            final ObjectNode jo = objectMapper.createObjectNode();
            jo.put("thirdPartyOrder", order.getThirdPartyOrder());
            jo.put("orderId", order.getOrderId());
            jo.put("state", 0);
            jo.put("resultCode", orderReport.getResultCode());
            jo.put("payAmount", orderReport.getPayAmount());
            jo.put("timeEnd", orderReport.getTimeEnd().toString());
            data.setNotifyBody(jo.toString());
            data.setNotifyUrl(notifyUrl);
            orderReportNotifyService.save(data);
            data.freeData();
        }
    }

    private void saveRefundReportNotify(OrderRefundReportDTO refundReportDto) {
        Order order = orderService.getById(refundReportDto.getOrderId());
        if (null == order) {
            throw new ApiException("订单不存在");
        }
        OrderRefund orderRefund = orderRefundService.getOne(refundReportDto.getOutRefundNo());
        if (null == orderRefund) {
            throw new ApiException("退款订单不存在");
        }
        String notifyUrl = orderRefund.getNotifyUrl();
        log.info("notifyUrl: {}", notifyUrl);
        if (StringUtils.hasLength(notifyUrl)) {
            OrderReportNotify data = new OrderReportNotify();
            data.setOrderId(orderRefund.getOrderId());
            data.setAddTime(LocalDateTime.now());
            data.setOutTradeNo(orderRefund.getTransactionId());
            data.setThirdPartyOrder(order.getThirdPartyOrder());
            final ObjectNode jo = objectMapper.createObjectNode();
            jo.put("thirdPartyOrder", order.getThirdPartyOrder());
            jo.put("orderId", order.getOrderId());
            jo.put("state", 1);
            jo.put("resultCode", refundReportDto.getRefundStatus());
            jo.put("payAmount", order.getPayAmount());
            jo.put("refundFee", refundReportDto.getRefundFee());
            jo.put("refundNo", refundReportDto.getRefundNo());
            jo.put("timeEnd", refundReportDto.getTimeEnd().toString());
            data.setNotifyBody(jo.toString());
            data.setNotifyUrl(notifyUrl);
            orderReportNotifyService.save(data);
            data.freeData();
        }
        order.freeData();
        orderRefund.freeData();
    }

    private OrderReport notifyWxOrder(OrgWxConfigDTO orgWxConfig, Map<String, String> params) {
        if (PaymentKit.verifyNotify(params, orgWxConfig.getApiKey())) {
            String resultCode = params.get("result_code");
            //总金额
            String totalFee = params.get("total_fee");
            String orderId = params.get("attach");
            //商户订单号
            String outTradeNo = params.get("out_trade_no");
            //微信支付订单号
            String transId = params.get("transaction_id");
            //支付完成时间
            String timeEnd = params.get("time_end");
            //支付银行
            String bankType = params.get("bank_type");

            Order order = orderService.getById(orderId);
            if (null == order) {
                log.info("订单不存在,orderId: " + orderId);
                throw new ApiException("订单不存在");
            }

            if (!order.getOutTradeNo().equals(outTradeNo)) {
                log.info("商户订单号异常,系统订单号: " + order.getOutTradeNo() + ",微信商户订单号: " + outTradeNo);
                throw new ApiException("商户订单号异常");
            }

            int payAmount = getPayAmount(totalFee);
            check(order, orgWxConfig, params, payAmount);

            order.freeData();


            OrderReport data = new OrderReport();
            data.setOrderId(orderId);
            data.setTransactionId(transId);
            data.setBankType(bankType);
            data.setResultCode(resultCode);
            try {
                LocalDateTime timeEndLocalDateTime = DateUtil.getDate(timeEnd);
                data.setTimeEnd(timeEndLocalDateTime);
            } catch (Exception e) {
                e.printStackTrace();
            }
            data.setAddTime(LocalDateTime.now());

            data.setPayAmount(new BigDecimal(payAmount));

            return data;
        } else {
            throw new ApiException("签名失败");
        }
    }

    private int getPayAmount(String totalFee) {
        int payAmount;
        try {
            payAmount = Integer.parseInt(totalFee);
        } catch (Exception e) {
            log.info("订单金额异常，totalFee: " + totalFee);
            throw new ApiException("订单金额异常");
        }
        return payAmount;
    }

    private void wxRespVerify(Map<String, String> params) {
        WxRespDTO wxRespDto = new WxRespDTO(params);
        try {
            if (wxRespDto.getReturnCode().equals(FAIL)) {
                throw new ApiException(wxRespDto.getReturnMsg());
            }
            if (StringUtils.hasLength(wxRespDto.getResultCode())) {
                if (wxRespDto.getResultCode().equals(FAIL)) {
                    String resultMsg = ERR_CODE + wxRespDto.getErrCode() + ",错误描述: " + wxRespDto.getErrCodeDes();
                    throw new ApiException(resultMsg);
                }
            }
        } finally {
            wxRespDto.freeData();
        }
    }

    private OrderRefundReportDTO notifyWxRefund(String reqInfo, OrgWxConfigDTO orgWxConfig) {
        String wxResult = AesUtil.decryptData(reqInfo, orgWxConfig.getApiKey());
        Map<String, String> params = PaymentKit.xmlToMap(wxResult);
        return getRefundWx(params);
    }

    private OrderRefundReportDTO getRefundWx(Map<String, String> params) {
        String outTradeNo = params.get("out_trade_no");
        Order order = orderService.getByOutTradeNo(outTradeNo);
        if (null == order) {
            log.info("订单不存在，商户订单号: " + outTradeNo);
            throw new ApiException("订单不存在，商户订单号: " + outTradeNo);
        }
        int i = -1;
        LocalDateTime addTime = LocalDateTime.now();
        OrderRefundReportDTO dto = getOrderRefundReportDto(order, params, addTime, i);
        createRefundWxReport(params, i, dto, i);
        dto.setXml(PaymentKit.toXml(params));
        return dto;
    }

    private void createRefundWxReport(Map<String, String> params, int intRefundCount, OrderRefundReportDTO orderRefundReport, int i) {
        OrderRefundWxReportDTO orderRefundWxReportDto;
        orderRefundWxReportDto = new OrderRefundWxReportDTO();
        orderRefundWxReportDto.setRid(orderRefundReport.getRid());
        orderRefundWxReportDto.setOrderId(orderRefundReport.getOrderId());
        orderRefundWxReportDto.setOutTradeNo(params.get("out_trade_no"));
        orderRefundWxReportDto.setTotalFee(new BigDecimal(params.get("total_fee")));
        String settlementTotalFee = params.get("settlement_total_fee");
        String settlementRefundFee = params.get("settlement_refund_fee");
        String refundAccount = params.get("refund_account");
        if (StringUtils.hasLength(settlementTotalFee)) {
            orderRefundWxReportDto.setSettlementTotalFee(new BigDecimal(settlementTotalFee));
        }
        String refundId = params.get("refund_id");
        if (i > 0) {
            orderRefundWxReportDto.setFeeType(params.get("fee_type"));
            String cashFee = params.get("cash_fee");
            if (StringUtils.hasLength(cashFee)) {
                orderRefundWxReportDto.setCashFee(Integer.parseInt(cashFee));
            }
            settlementRefundFee = params.get("settlement_refund_fee_" + i);
            orderRefundWxReportDto.setRefundCount(intRefundCount);
            refundId = params.get("refund_id_" + i);
            orderRefundWxReportDto.setRefundChannel(params.get("refund_channel" + i));
            orderRefundWxReportDto.setCouponType(params.get("coupon_type_" + i + "_" + i));
            String couponRefundFee = params.get("coupon_refund_fee_" + i);
            if (StringUtils.hasLength(couponRefundFee)) {
                orderRefundWxReportDto.setCouponRefundFee(Integer.parseInt(couponRefundFee));
            }
            String couponRefundCount = params.get("coupon_refund_count_" + i);
            if (StringUtils.hasLength(couponRefundCount)) {
                orderRefundWxReportDto.setCouponRefundCount(Integer.parseInt(couponRefundCount));
            }
            orderRefundWxReportDto.setCouponRefundId(params.get("coupon_refund_id_" + i + "_" + i));
            String couponRefundFeeOne = params.get("coupon_refund_fee_" + i + "_" + i);
            if (StringUtils.hasLength(couponRefundFeeOne)) {
                orderRefundWxReportDto.setCouponRefundFeeOne(Integer.parseInt(couponRefundFeeOne));
            }
            refundAccount = params.get("refund_account_" + i);
        }

        if (StringUtils.hasLength(settlementRefundFee)) {
            orderRefundWxReportDto.setSettlementRefundFee(Integer.parseInt(settlementRefundFee));
        }

        orderRefundWxReportDto.setRefundId(refundId);
        orderRefundWxReportDto.setRefundStatus(orderRefundReport.getRefundStatus());
        orderRefundWxReportDto.setRefundAccount(refundAccount);
        orderRefundWxReportDto.setRefundNo(orderRefundReport.getRefundNo());
        orderRefundReport.setOrderRefundWxReportDto(orderRefundWxReportDto);
    }

    private OrderRefundReportDTO getOrderRefundReportDto(Order order, Map<String, String> params, LocalDateTime addTime, int i) {
        OrderRefundReportDTO orderRefundReport;
        String refundStatus = params.get("refund_status");
        String outRefundNo = params.get("out_refund_no");
        String refundFee = params.get("refund_fee");
        String refundRecvAccout = params.get("refund_recv_accout");
        String successTime = params.get("success_time");
        String transactionId = params.get("transaction_id");
        if (i > -1) {
            refundStatus = params.get("refund_status_" + i);
            outRefundNo = params.get("out_refund_no_" + i);
            refundFee = params.get("refund_fee_" + i);
            refundRecvAccout = params.get("refund_recv_accout_" + i);
            successTime = params.get("refund_success_time_" + i);

        }
        OrderRefund orderRefund = orderRefundService.getOne(outRefundNo);
        if (null == orderRefund) {
            throw new ApiException("退款单不存在");
        }
        orderRefundReport = new OrderRefundReportDTO();

        orderRefundReport.setRid(orderRefund.getRid());
        orderRefundReport.setAddTime(addTime);
        orderRefundReport.setOrderId(order.getOrderId());
        orderRefundReport.setOutRefundNo(outRefundNo);
        orderRefundReport.setRefundNo(orderRefund.getRefundNo());
        orderRefundReport.setRefundFee(new BigDecimal(refundFee));
        orderRefundReport.setRefundRecvAccout(refundRecvAccout);
        orderRefundReport.setRefundStatus(refundStatus);
        orderRefundReport.setTransactionId(transactionId);
        if (refundStatus.equals(OrderService.SUCCESS)) {
            LocalDateTime timeEndLocalDateTime = DateUtil.getDate(successTime, "yyyy-MM-dd HH:mm:ss");
            orderRefundReport.setTimeEnd(timeEndLocalDateTime);
        }
        if (i == -1) {
            orderRefundReport.setRefundRequestSource(params.get("refund_request_source"));
        }
        return orderRefundReport;
    }

    private void check(Order dto, OrgWxConfigDTO orgWxConfig, Map<String, String> params, int payAmount) {
        String mchId = params.get("mch_id");
        String appId = params.get("appid");

        if (dto.getPayAmount().intValue() != payAmount) {
            log.info("订单金额不匹配，订单金额: " + dto.getPayAmount() + ",微信回执消息金额: " + payAmount);
            throw new ApiException("订单金额不匹配");
        }
        dto.freeData();

        if (!orgWxConfig.getMchId().equals(mchId)) {
            log.info("商户号不匹配,微信端MCH_ID： " + mchId + ",平台MCH_ID: " + orgWxConfig.getMchId());
            throw new ApiException("商户号不匹配");
        }

        String apiKey = orgWxConfig.getApiKey();
        if (!StringUtils.hasLength(apiKey)) {
            log.info("获得支付密钥失败，商户号: " + mchId + ",appId: " + appId);
            throw new ApiException("获得微信支付秘钥失败");
        }
    }

}